/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lbim.server.main;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Random;


import java.util.Set;

import java.util.logging.Level;
import lbim.server.data.MessageStore;
import lbim.server.model.Message;
import static lbim.server.main.LocateIMServer.accountManager;
import static lbim.server.main.LocateIMServer.currentSessions;
import static lbim.server.main.LocateIMServer.currentUsers;
import lbim.server.model.Request;
import lbim.server.model.Response;

import org.apache.mina.core.future.IoFutureListener;
import org.apache.mina.core.session.IoSession;
import org.kxml2.io.KXmlParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

/**
 *
 * @author Jestan
 */
public class MessageHandler {
    private final static Logger logger = LoggerFactory.getLogger(MessageHandler.class);
    private Random random;

    public MessageHandler() {
        random = new Random();
    }

    public void handleRequest(IoSession session, HttpRequestMessage httpRequestMessage) {

        String sid = httpRequestMessage.getRequestProperty("Session-Id");
        String requestType = httpRequestMessage.getRequestProperty("Request-Type");
        if (requestType != null && isValidSession(sid)) {
            int type = Integer.parseInt(requestType);
            switch (type) {
                case Request.LOG_IN:
                    logger.info("Login Request received");
                    handleLoginRequest(session, httpRequestMessage);
                    break;
                case Request.CHAT_MESSAGE:
                    logger.info("Chat Request received");
                    handleChatRequest(session, httpRequestMessage);
                    break;
                case Request.POOLING_MESSAGE:
                    logger.info("Pooling request received");
                    handlePoolingRequest(session, sid, httpRequestMessage);
                    break;
                case Request.LOCATION_UPDATE:
                    logger.info("Location update received");
                    handleLocationUpdate(session, sid, httpRequestMessage);
                    break;
                case Request.LOG_OUT:
                    logger.info("Logout Request received");
                    handleLogoutRequest(session, sid, httpRequestMessage);
                    break;
                case Request.PRESENCE_REQ_MESSAGE:
                    logger.info("Presence Request received");
                    handlePresenceRequest(session, sid, httpRequestMessage);
                    break;
                default:

            }
        } else {
            HttpResponseMessage response = new HttpResponseMessage();
            response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
            response.setProperty("Status", Integer.toString(Response.FAILED));
            StringBuffer sb = new StringBuffer();
            sb.append("<LbImMessage>")
                .append(" <Discription>Unknown Client</Discription>")
                .append("</LbImMessage>");
                response.appendBody(sb.toString());
            if (response != null) {
                session.write(response).addListener(IoFutureListener.CLOSE);
            }

        }

    }

    public void handleResponse() {
    }

    private void handleLoginRequest(IoSession session, HttpRequestMessage httpRequestMessage) {
        String message = httpRequestMessage.getRequestProperty("Xml-Message");
        String userName = "";
        String password = "";
        ByteArrayInputStream inputStream = new ByteArrayInputStream(message.getBytes());

        HttpResponseMessage response = new HttpResponseMessage();

        response.setProperty("Response-Type", Integer.toString(Response.LOG_IN));
        response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
        StringBuffer sb = new StringBuffer();

        try {
            InputStreamReader reader = new InputStreamReader(inputStream);
            KXmlParser parser = new KXmlParser();
            parser.setInput(reader);
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, "LbImMessage");
            while (parser.next() != XmlPullParser.END_DOCUMENT) {
                if (parser.getEventType() == XmlPullParser.START_TAG) {
                    if(parser.getName().equals("UserName")) {
                        userName = parser.nextText();
                    } else if(parser.getName().equals("Password")) {
                        password = parser.nextText();
                    }
                }
            }

                if (LocateIMServer.authenticate(userName, password)) {
                    // login success
                    logger.info("Login Success");
                    response.setProperty("Status", Integer.toString(Response.OK));
                    String sessionId = null;
                    long r = random.nextLong();
                    if(r < 0) {
                      sessionId = Long.toString(r).substring(1);
                    } else {
                      sessionId = Long.toString(r);
                    }

                    currentUsers.put(userName, sessionId);
                    currentSessions.put(sessionId, userName);
                    accountManager.updateStatus(userName, "ONLINE");

                    logger.info("User :"+userName+" sessionId :"+sessionId);

                    MessageStore.createInbox(sessionId);
                    //send  a presence update to friends
                    MessageStore.putPresenceUpdateMessage(sessionId, new Message(Response.PRESENCE_RESPONSE_MESSAGE, userName+":ONLINE"));

                    sb.append("<LbImMessage>")
                            .append("<SessionId>")
                            .append(sessionId)
                            .append("</SessionId>")
                            .append(" <Discription>Successfully connected to server</Discription>")
                            .append("</LbImMessage>");
                    response.appendBody(sb.toString());
                } else {
                    // login failed
                    response.setProperty("Status", Integer.toString(Response.FAILED));
                    System.out.println("Login failed");
                    sb.append("<LbImMessage>")
                            .append(" <Discription>Successfully connected to server</Discription>")
                            .append("</LbImMessage>");
                    response.appendBody(sb.toString());
                }

        } catch (Throwable e) {
            System.out.println(e);
        }
        finally {
            if (response != null) {
                session.write(response).addListener(IoFutureListener.CLOSE);
            }
        }

    }

    private void handleChatRequest(IoSession ioSession, HttpRequestMessage httpRequestMessage) {
        String sender = "";
        String receiver = "";

        String messageContent = httpRequestMessage.getRequestProperty("Xml-Message");
        ByteArrayInputStream inputStream = new ByteArrayInputStream(messageContent.getBytes());

        HttpResponseMessage response = new HttpResponseMessage();
        response.setProperty("Response-Type", Integer.toString(Response.DELIVERY_MESSAGE));

        InputStreamReader reader = new InputStreamReader(inputStream);

        logger.info("Processing chat request");

        try {
            KXmlParser parser = new KXmlParser();
            parser.setInput(reader);
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, "LbImMessage");
            while (parser.next() != XmlPullParser.END_DOCUMENT) {
                if (parser.getEventType() == XmlPullParser.START_TAG) {
                    if (parser.getName().equals("Sender")) {
                        sender = parser.nextText();
                    } else if (parser.getName().equals("Receiver")) {
                        receiver = parser.nextText();
                    }
                }
            }
            logger.info("Chat request, sender:"+sender+" receiver:"+receiver+" message:"+messageContent);

            String receiverSid = currentUsers.get(receiver);


            logger.info("Receiver's session id:"+receiverSid);

            if (MessageStore.putMessage(receiverSid, new Message(Response.CHAT_MESSAGE, messageContent))) {
                //user is online
                sendDeliveryStatus(ioSession, response);
                logger.info("chat message is ready to send");
            } else {
                //user is offline
                System.out.println("user:"+receiver+" is off line, so message will be discarded");
                response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
                response.setProperty("Status", Integer.toString(Response.FAILED));
                ioSession.write(response).addListener(IoFutureListener.CLOSE);
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void handleLogoutRequest(IoSession session, String sid, HttpRequestMessage httpRequestMessage) {
        HttpResponseMessage response = new HttpResponseMessage();
        try {
            response.setProperty("Response-Type", Integer.toString(Response.LOG_OUT));
            response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
            String userName = currentSessions.get(sid);

            currentSessions.remove(sid);
            currentUsers.remove(userName);

            accountManager.updateStatus(userName, "OFFLINE");

            //send  a presence update to friends
            MessageStore.putPresenceUpdateMessage(sid, new Message(Response.PRESENCE_RESPONSE_MESSAGE, userName+":OFFLINE"));
            
            logger.info("User :" + userName + " Session Id:" + sid + " logged out from the system");

            response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
            response.setProperty("Status", Integer.toString(Response.OK));

            StringBuffer sb = new StringBuffer();
            sb.append("<LbImMessage>")
              .append("<Discription>Successfully logged out</Discription>")
              .append("</LbImMessage>");
            response.appendBody(sb.toString());
        } catch (SQLException ex) {
            logger.error("erorr occured while persisting status for logout request", ex);
        } finally {
            if (response != null) {
                session.write(response).addListener(IoFutureListener.CLOSE);
            }
        }

    }

    private void handlePoolingRequest(IoSession session, String sid, HttpRequestMessage httpRequestMessage) {
        //it will deliver messages in the inbox, it can be chat, presence updates
        HttpResponseMessage response = new HttpResponseMessage();
        response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
        Message message = MessageStore.getMessage(sid);
        prepareToDeliver(response, message, sid);
        session.write(response).addListener(IoFutureListener.CLOSE);
    }

    private void handleLocationUpdate(IoSession session, String sid, HttpRequestMessage httpRequestMessage) {
        String message = httpRequestMessage.getRequestProperty("Xml-Message");
        ByteArrayInputStream inputStream = new ByteArrayInputStream(message.getBytes());

        HttpResponseMessage response = new HttpResponseMessage();
        response.setProperty("Response-Type", Integer.toString(Response.LOCATION_UPDATE));
        response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);

        try {
            String cellId = "";
            String log = "";
            String lat = "";
            String size = "";
            String zoom = "";
            InputStreamReader reader = new InputStreamReader(inputStream);
            KXmlParser parser = new KXmlParser();
            parser.setInput(reader);
            parser.nextTag();
            parser.require(XmlPullParser.START_TAG, null, "LbImMessage");
            while (parser.next() != XmlPullParser.END_DOCUMENT) {
                if (parser.getEventType() == XmlPullParser.START_TAG) {
                    if(parser.getName().equals("CellId")) {
                        cellId = parser.nextText();
                    }else if(parser.getName().equals("Logitude")) {
                        log = parser.nextText();
                    }else if(parser.getName().equals("Latitude")) {
                        lat = parser.nextText();
                    }else if(parser.getName().equals("Size")) {
                        size = parser.nextText();
                    }else if(parser.getName().equals("Zoom")) {
                        zoom = parser.nextText();
                    }
                }
            }
            String userName = currentSessions.get(sid);
            accountManager.updateLocation(userName,lat+","+log, "ONLINE");
            logger.info("CellId:"+ cellId+" Latitude:"+ lat+ " Longitude:"+ log+ "Size:"+size + "Zoom:"+zoom );
            response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
            response.setProperty("Status", Integer.toString(Response.OK));         
            String markers = accountManager.getMarkers();
            logger.info("markers :"+ markers);
            byte[] data = MapService.getMap(lat+","+log , size, markers, zoom);
            response.setProperty("Content-Length", ""+data.length);
            response.appendBody(data);
        } 
        catch (Exception ex) {
            response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
            response.setProperty("Status", Integer.toString(Response.FAILED));
            logger.error("exception occured while proccessing location update ",ex);
        }finally {
            if (response != null) {
                session.write(response).addListener(IoFutureListener.CLOSE);
            }
        }

    }

    private boolean isValidSession(String sid) {
        if(sid == null) {
            return false;
        }
        else if(sid.equals("null")) {
            return true;
        } else {
            return currentUsers.containsValue(sid);
        }
    }


    private void prepareToDeliver(HttpResponseMessage response, Message message,  String sid) {
        response.setProperty("Response-Type", Integer.toString(message.getType()));
        response.setProperty("Status", Integer.toString(Response.OK));
        switch(message.getType()) {
            case Response.CHAT_MESSAGE:
                encodeChatMessage(response, message);
                break;
            case Response.PRESENCE_RESPONSE_MESSAGE:
                encodePresenceUpdate(response, sid);
                break;
        }

    }

    private void encodeChatMessage(HttpResponseMessage response, Message message) {
        response.appendBody(message.getContent());
    }

    private void encodePresenceUpdate(HttpResponseMessage response,String sid) {
        try {
            String userName = currentSessions.get(sid);
            String presenceList = accountManager.getPresence(userName);
            StringBuffer sb = new StringBuffer();
            sb.append("<LbImMessage>")
                    .append("<PresenceList>")
                    .append(presenceList)
                    .append("</PresenceList>")
                    .append("</LbImMessage>");
            response.appendBody(sb.toString());
            logger.info("Sending precence list :" + presenceList);

        } catch (Throwable e) {
            System.out.println(e);
        }
    }

    private void sendDeliveryStatus(IoSession ioSession, HttpResponseMessage response) {
        response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
        response.setProperty("Status", Integer.toString(Response.OK));
        ioSession.write(response).addListener(IoFutureListener.CLOSE);
    }


    private void handlePresenceRequest(IoSession session, String sid,HttpRequestMessage httpRequestMessage) {
        HttpResponseMessage response = new HttpResponseMessage();
        response.setResponseCode(HttpResponseMessage.HTTP_STATUS_SUCCESS);
        response.setProperty("Status", Integer.toString(Response.OK));
        response.setProperty("Response-Type", Integer.toString(Response.PRESENCE_RESPONSE_MESSAGE));

        try {
            String userName = currentSessions.get(sid);
            String presenceList = accountManager.getPresence(userName);
            StringBuffer sb = new StringBuffer();
            sb.append("<LbImMessage>")
              .append("<PresenceList>")
              .append(presenceList)
              .append("</PresenceList>")
              .append("</LbImMessage>");
            response.appendBody(sb.toString());
            logger.info("Sending precence list :"+presenceList);

        } catch (Throwable e) {
            System.out.println(e);
        }
        finally {
            if (response != null) {
                session.write(response).addListener(IoFutureListener.CLOSE);
            }
        }

    }
}
